﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;

namespace Mefisto4.Core.MEF
{
    [Export(typeof(IControllerFactory))]
    public class MEFControllerFactory : IControllerFactory
    {
        #region Properties

        public CompositionContainer Container
        {
            get { return CompositionContainerFactory.CreateContainer(); }
        }

        #endregion

        //#region Constructor

        //public MEFControllerFactory()
        //{
        //    Container = DependencyResolver.Current.GetService<CompositionContainer>();
        //}

        //#endregion

        #region Methods

        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            // Filter by namespace (if available on the route)
            IEnumerable<string> namespaces = GetNamespaceFromRoute(requestContext);

            //If there's a module given, let's filter on it
            if (requestContext.RouteData.Values.ContainsKey("module"))
                namespaces =  new []{ requestContext.RouteData.Values["module"].ToString() }.AsEnumerable();

            var export = Container.GetExports<IController, IDictionary<String, object>>()
                                  .FirstOrDefault(exp => exp.Metadata
                                                            .ContainsKey(Constants.ControllerNameMetadataName)
                                                      && exp.Metadata[Constants.ControllerNameMetadataName]
                                                            .ToString()
                                                            .ToLowerInvariant()
                                                            .Equals(controllerName.ToLowerInvariant())
                                                      && (namespaces == null
                                                        || !namespaces.Any()
                                                        || namespaces.Any(n => exp.Metadata[Constants.ControllerNamespaceMetadataName]
                                                                                  .ToString()
                                                                                  .ToLowerInvariant()
                                                                                  .StartsWith(n.ToLowerInvariant()))));

            if (export == null)
            {
                throw new HttpException(404, "Not found");
            }

            requestContext.HttpContext.Items[Constants.ControllerExportEntryName] = export;

            var controller = export.Value;

            // ITempDataProvider
            /*var tempDataProvider = DependencyResolver.Current.GetService<ITempDataProvider>();
            if (tempDataProvider != null)
                ((Controller)controller).TempDataProvider = tempDataProvider;
            */
            return controller;
        }

        public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
        {
            return SessionStateBehavior.Default;
        }

        public void ReleaseController(IController controller)
        {
            //var export = HttpContext.Current.Items[Constants.ControllerExportEntryName] as Lazy<IController>;

            //if (export != null)
            //{
            //    Container.ReleaseExport(export);
            //}
        }

        #endregion

        #region Internal Methods

        private static IEnumerable<string> GetNamespaceFromRoute(RequestContext requestContext)
        {
            object routeNamespacesObj;

            if (requestContext != null &&
                requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj))
            {
                return routeNamespacesObj as IEnumerable<string>;
            }

            return null;
        }

        #endregion
    }
}